#include "StdAfx.h"
//-------------------------------------------------------------------------------------------------
//ImageProcSteps.cpp  - Colleciton of Image Processing Steps for an Experiment       
//-------------------------------------------------------------------------------------------------
//FreeBSD License
//Copyright 2012 Will Stott All rights reserved.
//
//Redistribution and use in source and binary forms, with or without modification, are
//permitted provided that the following conditions are met:
//
//   1. Redistributions of source code must retain the above copyright notice, this list of
//      conditions and the following disclaimer.
//
//   2. Redistributions in binary form must reproduce the above copyright notice, this list
//      of conditions and the following disclaimer in the documentation and/or other materials
//      provided with the distribution.
//
//THIS SOFTWARE IS PROVIDED BY Will Stott ``AS IS'' AND ANY EXPRESS OR IMPLIED
//WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
//FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Will Stott OR
//CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
//CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
//SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
//ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//The views and conclusions contained in the software and documentation are those of the
//authors and should not be interpreted as representing official policies, either expressed
//or implied, of Will Stott.
//
//----Authors--------------------------------------------------------------------------------------
//created by: will.stott@ucl.ac.uk Feb 2012
//
//----Comments-------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------

#include "tinyxml.h"
#include "TinyXmlHelper.h"
#include "ImageExptLib.h"

#include "ImageProcessFile.h"
#include "ImageProcSteps.h"
#include "ImageProcStepAnalysis.h"

const char * const ImageProcSteps::xmlProcSteps = "ProcSteps";
const char * const ImageProcSteps::BadImagePathFilename = "[NoFileSet]";
const char * const ImageProcSteps::BadImageIntermedPathFilename = "[NoIntermedFileSet]";

ImageProcSteps::ImageProcSteps(void) :
	_error(true),
	_selImage('\0'),
	_NextStepPos(0),
	_ImagePathFilename(ImageProcSteps::BadImagePathFilename),
	_ImageIntermedPathFilename(ImageProcSteps::BadImageIntermedPathFilename),
	_pStrFilePathNameBuffer('\0'),
	_pwStrFilePathNameBuffer('\0'),
	_pwStrImageIntermedPathFilename('\0')
{
							 //allocate wchar_t * buffer
	if ( (_pwStrFilePathNameBuffer = (wchar_t *) ::malloc((ImageExptLib::MaxPathFileNameLen+1) * sizeof(wchar_t))) == '\0')
		ImageExptLib::LogMsg("ImageProcSteps::ImageProcSteps", "ERROR: malloc failed for _pwStrFilePathNameBuffer");
	else
		ZeroMemory( _pwStrFilePathNameBuffer, sizeof( (ImageExptLib::MaxPathFileNameLen+1) * sizeof(wchar_t)) );

							//allocate char * buffer
	if ( (_pStrFilePathNameBuffer = (char *) ::malloc((ImageExptLib::MaxPathFileNameLen+1) * sizeof(char))) == '\0')
		ImageExptLib::LogMsg("ImageProcSteps::ImageProcSteps", "ERROR: malloc failed for _pStrFilePathNameBuffer");
	else
		ZeroMemory( _pStrFilePathNameBuffer, sizeof( (ImageExptLib::MaxPathFileNameLen+1) * sizeof(char)) );

							 //allocate wchar_t * buffer for intermediate path-filename
	if ( (_pwStrImageIntermedPathFilename =  (wchar_t *) ::malloc((ImageExptLib::MaxPathFileNameLen+1) * sizeof(wchar_t))) == '\0')
		ImageExptLib::LogMsg("ImageProcSteps::ImageProcSteps", "ERROR: malloc failed for_pwStrImageIntermedPathFilename");
	else
		ZeroMemory( _pwStrImageIntermedPathFilename, sizeof( (ImageExptLib::MaxPathFileNameLen+1) * sizeof(wchar_t)) );
}

ImageProcSteps::~ImageProcSteps(void)
{
	ClearAllSteps();

	if (_pStrFilePathNameBuffer != 0 )				//free char * buffer
		::free(_pStrFilePathNameBuffer);
	_pStrFilePathNameBuffer = '\0';
	if (_pwStrFilePathNameBuffer != '\0')			//free wchar_t * buffer
		::free(_pwStrFilePathNameBuffer);
	_pStrFilePathNameBuffer = '\0';
	if (_pwStrImageIntermedPathFilename != '\0')	//free wchar_t * _pwStrImageIntermedPathFilename
		::free(_pwStrImageIntermedPathFilename);
	_pwStrImageIntermedPathFilename = '\0';
}

bool	ImageProcSteps::ClearAllSteps()
{
	bool	rc = false;

	size_t cnt = _Steps.size();
	if (cnt > 0 )
	{
		for (size_t x = 0; x < cnt; x++)
			delete  _Steps[x];
		_Steps.clear();
		rc = true;
	}
	_error = true;

	return rc;
}

bool	ImageProcSteps::Validate()
{
	bool rc = false;

	int cnt = (int)_Steps.size();
	if ( cnt <=  0 )
		ImageExptLib::LogMsg("ImageProcSteps::Validate", "ERROR: Step list is empty");
	else
	{
		int AnalysisStepCnt = 0;
		rc = true;
		for (int x = 0; x < cnt; x++ )
		{
			ImageProcStep *p = GetStepPrivate(x);
			if ( p == '\0')
			{
				ImageExptLib::LogMsg("ImageProcSteps::Validate", "ERROR: Step list does not contain step %d", x);
				rc = false;
				break;
			}
			else
			{
				if (p->IsValid() == false)
				{
					ImageExptLib::LogMsg("ImageProcSteps::Validate", "ERROR: step %d (%s) is invalid",  x, p->GetFunctionName() );
					rc = false;
					break;
				}
				if (p->IsAnalysisStep())
					AnalysisStepCnt++;
			}
		}
		if (AnalysisStepCnt != 1)		//steps must contain just one Analysis Step
		{
			ImageExptLib::LogMsg("ImageProcSteps::Validate", "ERROR: Analysis Step not found");
			rc = false;
		}
		if (ImageExptLib::IsFileExist(_pwStrImageIntermedPathFilename) == false)
		{
			ImageExptLib::LogMsg("ImageProcSteps::Validate", "ERROR: IsFileExist(ImageIntermedPathFilename) - use Tools | Options to set an existing file to use for intermediate image processing");
			rc = false;
		}
	}
	if (rc == true)
		_error = false;

	return rc;
}

bool	ImageProcSteps::LoadSteps(ImageExpt *ief, ImageProcStepLibs *procLibs, const wchar_t *ImageIntermedPathFilename )
{
	bool	rc = false;

	_error = true;

	int LoadCnt = 0;
	if ((ief == '\0') || (procLibs == '\0') || (ImageIntermedPathFilename == '\0'))
		ImageExptLib::LogMsg("ImageProcSteps::LoadSteps", "ERROR: input param ief is %s, procLibs is %s ImageIntermedPathFilename is %s", ((ief == '\0') ? "null" : "not null"), ((procLibs == '\0') ? "null" : "not null"), ((ImageIntermedPathFilename == '\0') ? "null" : "not null"));
	else
	{
		if (SetImageIntermediatePathFilename(ImageIntermedPathFilename) == false)
			ImageExptLib::LogMsg("ImageProcSteps::LoadSteps", "ERROR: failed to set _pwStrImageIntermedPathFilename and _ImageIntermedPathFilename from passed param");
		else
		{
			int ExpectCnt = 0;
			if( (ExpectCnt = TinyXmlHelper::GetChildElementCnt(ief->GetHeaderProcSteps(), ImageProcStep::xmlProcStep)) < 1 )
				ImageExptLib::LogMsg("ImageProcSteps::LoadSteps", "ERROR: %d steps found in ief=%s", ExpectCnt, ief->GetPathFileName());
			else
			{
				std::string val;
				TiXmlElement *stepXml = '\0';
				for( stepXml = TinyXmlHelper::FindChildXmlElement(ief->GetHeaderProcSteps(), ImageProcStep::xmlProcStep); stepXml != '\0'; stepXml = stepXml->NextSiblingElement() )
				{
					val = stepXml->Value();
					if ( val != ImageProcStep::xmlProcStep)
					{
						ImageExptLib::LogMsg("ImageProcSteps::LoadSteps", "ERROR: xml bad, found <%s> rather than <%s>", val, ImageProcStep::xmlProcStep);
						break;
					}
					else
					{
						int stepNum = 0;
						if (stepXml->Attribute(ImageProcStep::attribStepNum, &stepNum) == '\0')
							ImageExptLib::LogMsg("ImageProcSteps::LoadSteps", "ERROR: xml bad, <%s %s=(not found)>", ImageProcStep::xmlProcStep, ImageProcStep::attribStepNum);
						else
						{
							if (AddStep(stepXml, procLibs) == false)
							{
								ImageExptLib::LogMsg("ImageProcSteps::LoadSteps", "ERROR: xml bad, cannot load  <%s %s=%d>", ImageProcStep::xmlProcStep, ImageProcStep::attribStepNum, stepNum);
								break;
							}
							else
								LoadCnt++;
						}
					}
				}
				if ( LoadCnt != ExpectCnt )
					ImageExptLib::LogMsg("ImageProcSteps::LoadSteps", "ERROR: xml bad, <%s> %d found, but %d loaded", ImageProcSteps::xmlProcSteps, ExpectCnt, LoadCnt);
				else
				{
					if ( Validate() == true)
					{
						_error = false;
						rc = true;
					}
				}
			}
		}
	}
	return rc;
}

bool ImageProcSteps::SaveAnalysisToSelImageXml()
{
	bool rc = false;

	size_t cnt = _Steps.size();
	if (cnt > 0 )
	{
		rc = true;
		for (size_t x = 0; x < cnt; x++)
		{
			if (_Steps[x]->SaveResultsToXml(_selImage) == false)
			{
				ImageExptLib::LogMsg("ImageProcSteps::SaveAnalysisToSelImageXml()", "ERROR: step %d outputs not saved", x );
				rc = false;
				break;
			}
		}
	}
	return rc;
}

bool ImageProcSteps::SaveSettingsToXml(TiXmlElement *procStepsXml)
{
	bool rc = false;

	if ( procStepsXml == '\0')
		ImageExptLib::LogMsg("ImageProcSteps::SaveSettingsToXml()", "ERROR: procStepsXml is null" );
	else
	{
		size_t cnt = _Steps.size();
		if (cnt > 0 )
		{
			size_t x = 0;
			for (x = 0; x < cnt; x++)
			{
				if (_Steps[x]->SaveSettingsToXml(procStepsXml) == false)  
				{
					ImageExptLib::LogMsg("ImageProcSteps::SaveSettingsToXml()", "ERROR: step %d outputs not saved", x );
					break;
				}
			}
			if (x != cnt)
				ImageExptLib::LogMsg("ImageProcSteps::SaveSettingsToXml()", "ERROR: only saved %d of %d Steps", x, cnt );
			else
			{
				rc = true;
			}
		}
	}
	return rc;
}

int	 ImageProcSteps::GetStepCount() 
{ 
	int rc = ImageProcSteps::InvalidStepCount;
	if (IsValid() == false)
		ImageExptLib::LogMsg("ImageProcSteps::GetStepCount", "ERROR: steps have not been validated" );
	else
		rc = static_cast<int>(_Steps.size()); 

	return rc;
}

ImageProcStep  *ImageProcSteps::GetStep(int StepNum)
{
	ImageProcStep  *rc = '\0';

	if (IsValid() == false)
		ImageExptLib::LogMsg("ImageProcSteps::GetStep(int)", "ERROR: steps have not been validated" );	
	else
	{
		if ((StepNum < 0) || (StepNum >= (int)_Steps.size()))
			ImageExptLib::LogMsg("ImageProcSteps::GetStep(int)", "ERROR: invalid param SeqNo=%d (steps=%d)", StepNum, (int)_Steps.size()  );
		else
		{
			rc = GetStepPrivate(StepNum);
			if (rc == '\0') 
				ImageExptLib::LogMsg("ImageProcSteps::GetStep(int)", "ERROR: GetStep(SeqNo=%d) not found (steps=%d)", StepNum, (int)_Steps.size()  );
		}
	}
	return rc;
}

ImageProcStep  *ImageProcSteps::GetNextStep()
{
	ImageProcStep  *rc = '\0';

	if ( IsValid() == false)
		ImageExptLib::LogMsg("ImageProcSteps::GetNextStep", "ERROR: steps have not been validated" );	
	else
	{
		size_t cnt = _Steps.size();
		if ((cnt <= 1 ) ||  (_NextStepPos < 1 ))
			ImageExptLib::LogMsg("ImageProcSteps::GetNextStep", "ERROR: _NextStepPos=%d invalid (range 1-%d)", _NextStepPos, cnt );	
		else
		{
			if ( _NextStepPos < cnt )
			{
				rc = GetStep(_NextStepPos);		//if cnt == 1, GetStep returns NULL so expt terminates
				_NextStepPos++;
			}
		}
	}
	return rc;
}

ImageProcStep   *ImageProcSteps::GetFirstStep(const wchar_t *ImageDir, TiXmlElement  *image)
{
	ImageProcStep  *rc = '\0';

	_NextStepPos = 0;

	if ( IsValid() == false)
		ImageExptLib::LogMsg("ImageProcSteps::GetFirstStep", "ERROR: steps have not been validated" );	
	else
	{
		if ((ImageDir == '\0') || (image == '\0'))
			ImageExptLib::LogMsg("ImageProcSteps::GetFirstStep", "ERROR: params ImageDir=%s or image=%s is null", (ImageDir == '\0') ? "null" : "non null",  (image == '\0') ? "null" : "non null" );	
		else
		{
			size_t cnt = _Steps.size();
			if ( cnt <= 0 )
				ImageExptLib::LogMsg("ImageProcSteps::GetFirstStep", "ERROR: no steps found %d", (int)cnt );
			else
			{
				const char *ImageDesc = image->GetText();
				if (ImageDesc == '\0')
					ImageDesc = ImageProcessFile::xmlNoImageFilename;
				if ( SetImagePathFilename(ImageDir, image) == false )
					ImageExptLib::LogMsg("ImageProcSteps::GetFirstStep", "ERROR: not able to set Image file %s\\%s", ImageDir, ImageDesc );
				else
				{
					if ((rc = GetStep(0)) == '\0')
						ImageExptLib::LogMsg("ImageProcSteps::GetFirstStep", "ERROR: GetStep(0) failed");
					else
						_NextStepPos = 1;
				}
			}
		}
	}
	return rc;
}

bool	ImageProcSteps::AddStep(TiXmlElement *stepXml, ImageProcStepLibs *procLibs)
{
	bool rc = false;

	if ((stepXml == '\0') || (procLibs == '\0'))
		ImageExptLib::LogMsg("ImageProcSteps::AddStep(stepXml,libs)", "ERROR: input param stepXml is %s, procLibs is %s", ((stepXml == '\0') ? "null" : "not null"), ((procLibs == '\0') ? "null" : "not null"));
	else
	{
		ImageProcStep *step = '\0';
		std::string yes(ImageProcStep::attribStepAnalysisYes);

		const char *Analysis = stepXml->Attribute(ImageProcStep::attribStepAnalysis);
	//	if ((Analysis != '\0') && (yes == Analysis))
	//		step = new ImageProcStepAnalysis(stepXml, procLibs);
	//	else
			step = new ImageProcStep(stepXml, procLibs);


		if (step == '\0') 
			ImageExptLib::LogMsg("ImageProcSteps::AddStep(stepXml)", "ERROR: new ImageProcStep is null");
		else
		{
			rc = AddStep(step);
		}
		if ((rc == false) && (step != '\0'))
			delete step;
	}
	return rc;
}

bool	ImageProcSteps::IsExisting(ImageProcStep *step)
{
	bool rc = false;

	if (step != '\0')
	{
		if (GetStepPrivate(step->GetStepNum()) != '\0') 
			rc = true;
	}
	return rc;
}

bool	ImageProcSteps::AddStep(ImageProcStep *step)		 
{
	bool	rc = false;

	if (step == '\0')
		ImageExptLib::LogMsg("ImageProcSteps::AddStep(ImageProcStep)", "ERROR: param step is null");		
	else		
	{
		if (step->IsValid() == false)
			ImageExptLib::LogMsg("ImageProcSteps::AddStep", "ERROR: param step function (%s) is invalid", step->GetFunctionName());
		else
		{
			if ( GetStepPrivate(step->GetStepNum()) != '\0')
				ImageExptLib::LogMsg("ImageProcSteps::AddStep", "ERROR: param step=%d already in list of steps", step->GetStepNum());
			else
			{
				//set _error = true; //not necessary as effectively all Validate() does is to invoke step->IsValid() which has been done above
				_Steps.push_back(step);
				rc = true;
			}
		}
	}
	return rc;
}

bool 	ImageProcSteps::CopyAdd(ImageProcStep *step)
{
	bool rc = false;

	if (step == '\0')
		ImageExptLib::LogMsg("ImageProcSteps::ImageProcSteps::CopyAdd()", "ERROR: passed param is null" );
	else
	{
		ImageProcStep *copy = '\0';
	//	if (step->IsAnalysisStep() )
	//		copy = new ImageProcStepAnalysis((ImageProcStepAnalysis *)step);
	//	else
			copy = new ImageProcStep(step);
		if (AddStep(copy) == false)
		{
			ImageExptLib::LogMsg("ImageProcSteps::ImageProcSteps::CopyAdd()", "ERROR: failed to add copy step %s to list", (copy != '\0') ? copy->GetFunctionName() : "null" );
			delete copy;
		}
		else
			rc = true;
	}
	return rc;
}

bool	ImageProcSteps::SetImageIntermediatePathFilename(const wchar_t *ImageIntermedPathFilename)
{
	bool rc = false;

	if ((_pwStrImageIntermedPathFilename == '\0') || (_pStrFilePathNameBuffer == '\0') || (ImageIntermedPathFilename == '\0'))
		ImageExptLib::LogMsg("ImageProcSteps::SetImageIntermediatePathFilename", "ERROR: buffers or passed param are null" );
	else
	{		
		if (ImageExptLib::IsFileExist(ImageIntermedPathFilename) == false)
			ImageExptLib::LogMsg("ImageProcSteps::SetImageIntermediatePathFilename", "ERROR: IsFileExist(ImageIntermedPathFilename) - use Tools | Options to set an existing file to use for intermediate image processing");
		else
		{						//save passed param wchar_t * ImageIntermedPathFilename	in _pwStrImageIntermedPathFilename
			if (wcscpy_s( _pwStrImageIntermedPathFilename,  ImageExptLib::MaxPathFileNameLen, ImageIntermedPathFilename ) != 0 )
				ImageExptLib::LogMsg("ImageProcSteps::SetImageIntermediatePathFilename", "ERROR: wcscpy_s failed for_pwStrImageIntermedPathFilename");
			else
			{					//convert passed param wchar_t * ImageIntermedPathFilename to char *  
				if (ImageExptLib::ConvertwStrToStr(_pStrFilePathNameBuffer, ImageExptLib::MaxPathFileNameLen, _pwStrImageIntermedPathFilename, ImageExptLib::MaxPathFileNameLen) == '\0')
				{
					ImageExptLib::LogMsg("ImageProcSteps::SetImageIntermediatePathFilename", "ERROR: ImageExptLib::ConvertwStrToStr() failed");
					ZeroMemory( _pwStrImageIntermedPathFilename, sizeof( (ImageExptLib::MaxPathFileNameLen+1) * sizeof(wchar_t)) );
				}
				else			//save passed param wchar_t * ImageIntermedPathFilename	in std::string _ImageIntermedPathFilename
				{
					_ImageIntermedPathFilename = _pStrFilePathNameBuffer;
					rc = true;
				}
			}
		}
	}

	return rc;
}

bool	ImageProcSteps::SetImagePathFilename(const wchar_t *ImageDir, TiXmlElement  *image)
{
	bool rc = false;

	if (IsValid() == false)
		ImageExptLib::LogMsg("ImageProcSteps::SetImagePathFilename", "ERROR: IsValid() fails - cannot call SetImageIntermediatePathFilename() before Validate()" );
	else
	{
		if ((image == '\0') || (ImageDir == '\0') || (_pwStrImageIntermedPathFilename == '\0') || (wcsnlen(_pwStrImageIntermedPathFilename, ImageExptLib::MaxPathFileNameLen ) < ImageExptLib::MinPathFileNameLen))
			ImageExptLib::LogMsg("ImageProcSteps::SetImagePathFilename", "ERROR: input param ImageDir is null or image is null or intermediate image file not valid");
		else
		{
			std::string elementNameImage(ImageProcessFile::xmlImage);
			if ( image->Value() != elementNameImage)
				ImageExptLib::LogMsg("ImageProcSteps::SetImagePathFilename", "ERROR: input param image is <%s> not <%s>", image->Value(), elementNameImage.c_str());
			else
			{
				if ( (image->GetText() == '\0' ) || (strlen(image->GetText()) == 0))
					ImageExptLib::LogMsg("ImageProcSteps::SetImagePathFilename", "ERROR: image->GetText() returns null or empty");
				else
				{
					if (ImageExptLib::ConvertwStrToStr(_pStrFilePathNameBuffer, ImageExptLib::MaxPathFileNameLen, ImageDir, ImageExptLib::MaxPathFileNameLen) == '\0')
						ImageExptLib::LogMsg("ImageProcSteps::SetImagePathFilename", "ERROR: ImageExptLib::ConvertwStrToStr() failed");
					else			
					{
						_ImagePathFilename = _pStrFilePathNameBuffer;
						_ImagePathFilename += "\\";
						_ImagePathFilename += image->GetText();
						_ImagePathFilename += ImageExptLib::ImageFileType;

						const wchar_t *imagefilename = '\0';
						if ( (imagefilename = ImageExptLib::ConvertStrTowStr(_pwStrFilePathNameBuffer, ImageExptLib::MaxPathFileNameLen, _ImagePathFilename.c_str(), ImageExptLib::MaxPathFileNameLen)) == '\0')
							ImageExptLib::LogMsg("ImageProcSteps::SetImagePathFilename", "ERROR: failed to convert image pathfilename %s", _ImagePathFilename.c_str());
						else
						{
							if (ImageExptLib::IsFileExist(_pwStrFilePathNameBuffer) == false)
								ImageExptLib::LogMsg("ImageProcSteps::SetImagePathFilename", "ERROR: input param image pathfilename not found %s", _ImagePathFilename.c_str());
							else
							{
								if ( ImageExptLib::CopyImageFile(_pwStrImageIntermedPathFilename, _pwStrFilePathNameBuffer) == false)
									ImageExptLib::LogMsg("ImageProcSteps::SetImagePathFilename", "ERROR: ImageExptLib::CopyFile(%s, %s)", _ImageIntermedPathFilename.c_str(), _ImagePathFilename.c_str());
								else
								{
									if (ImageExptLib::IsFileExist(_pwStrImageIntermedPathFilename) == false)
										ImageExptLib::LogMsg("ImageProcSteps::SetImagePathFilename", "ERROR: copied intermediate pathfilename not found %s", _ImageIntermedPathFilename.c_str());
									else
									{
										_selImage = image;
										rc = true;
									}
								}
							}
						}
					}
				}
			}
		}
	}
	return rc;
}

//-------------------------------------------------------------------------------------------------
//			PRIVATE
//-------------------------------------------------------------------------------------------------

ImageProcStep   *ImageProcSteps::GetStepPrivate(int StepNum)
{
	ImageProcStep   *rc = '\0';

	size_t cnt = _Steps.size();
	if (cnt > 0 )
	{
		for (size_t x = 0; x < cnt; x++)
		{
			if (_Steps[x]->GetStepNum() == StepNum)
			{
				rc = _Steps[x];
				break;
			}
		}
	}
	return rc;
}



//-------------------------------------------------------------------------------------------------
//			E N D   O F   F I L E
//-------------------------------------------------------------------------------------------------

